module(..., package.seeall)
require'zdc.doc'
require'zwsapi.app'
require("coxpcall")
require("wsapi.request")
require("wsapi.response")


-----------------------------------------------------------------------------
-- Template for an error messages with a stack trace.
-----------------------------------------------------------------------------
local HTML_MESSAGE_WITH_STACK_TRACE = [[
<br/>
<span style="color:red; font-size: 19pt;">%s</span></br><br/><br/>
Error details: <pre><b><code>%s</code></b></pre><br/>
]]

-----------------------------------------------------------------------------
-- Template for an error messages without a stack trace.
-----------------------------------------------------------------------------
local HTML_MESSAGE_WITHOUT_STACK_TRACE = [[
<br/>
<span style="color:red; font-size: 19pt;">%s</span></br><br/><br/>
(If you are the admin for this site, you can turn on stack trace display
 by setting SHOW_STACK_TRACE parameter to true.)
]]

-----------------------------------------------------------------------------
-- Template for the error messages when error handling failed...
-----------------------------------------------------------------------------
ULTIMATE_OOPS_MESSAGE = [[
 <br/><br/><br/><br/>
 <center><b>Something went terribly wrong...</b></center>
]]


-----------------------------------------------------------------------------
-- Given two apps, creates a new one that uses the second app as a backup for
-- the first. So, the first app gets called first. If it runs successfully,
-- it's output is returned. If it fails, the second app is called for error
-- handling.
-----------------------------------------------------------------------------
local function make_safer_app(app_function, error_app_function)
	return function(wsapi_env, err)
		local ok, status, headers, fn = coxpcall(
					function() return app_function(wsapi_env, err) end,
					function(e) return error_app_function(wsapi_env, e) end
				)
		return status, headers, fn
	end
end

local _HTTP_INFOLST=
{
	GATEWAY_INTERFACE    = "CGI/1.1",
	HTTP_ACCEPT = "application/xml,application/xhtml+xml,text/html;q=0.9," ..
			"text/plain;q=0.8,image/png,*/*;q=0.5",
	HTTP_ACCEPT_CHARSET  = "ISO-8859-1,utf-8;q=0.7,*;q=0.3",
	HTTP_ACCEPT_ENCODING = "gzip,deflate,sdch",
	HTTP_ACCEPT_LANGUAGE = "en-US,en;q=0.8",
	HTTP_CACHE_CONTROL   = "max-age=0",
	HTTP_CONNECTION      = "keep-alive",
	HTTP_HOST            = "127.0.0.1:80",
	HTTP_USER_AGENT      = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X " ..
			"10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) " ..
			"Chrome/6.0.472.55",
	HTTP_VERSION         = "HTTP/1.1",
	REMOTE_ADDR          = "127.0.0.1",
	REMOTE_HOST          = "localhost",
	SCRIPT_NAME          = "wsapi_test",
	SERVER_NAME          = "localhost",
	SERVER_PORT          = "80",
	SERVER_PROTOCOL      = "HTTP/1.1",
}
local function test_mock(request,response)
	response:write((zdc.doc.str_print_dbg('<pre>',request,'</pre>')))
	response.status = 500
	return response:finish()
end

-----------------------------------------------------------------------------
-- Creates an optimistic WSAPI app function for app. This function
-- assumes that everything will work out right. We'll have to wrap this with
-- make_safer_app() to make sure the errors get reported properly.
-----------------------------------------------------------------------------
local function make_basic_zapp(modnew,config,apachefcgi)
	local modnew=modnew or zwsapi.app.new
	local my_zapp = modnew(config)
	if config.INIT_FUNCTION then
		config.INIT_FUNCTION(my_zapp)
	end
	return function (wsapi_env)
		local _tm_start=os.clock()
		local request = wsapi.request.new(wsapi_env)
		--if apachefcgi then
		--	request.path_info=wsapi_env.SCRIPT_URL or wsapi_env.REQUEST_URI
		--end
		request.ip = wsapi_env.REMOTE_ADDR
		for k,v in pairs(_HTTP_INFOLST) do
			local _=wsapi_env[k]
		end

		local response = wsapi.response.new()
		
		--do return test_mock(request,response) end
		
		--xpcall(function ()my_zapp:WSHandleRequest(request, response)end,print)
		my_zapp:WSHandleRequest(request, response)
		if response.headers["Location"] then
			if response.status < 300 then
				response.status = 302
			end
		end
		--print("cpu:"..(os.clock()-_tm_start),request.path_info..'?'..request.query_string)
		return response:finish()
	end
end


-----------------------------------------------------------------------------
-- An auxiliary functions to catch common errors and provide a better message
-- for them.
-----------------------------------------------------------------------------
local detect_common_errors = function(error_message, config)
	return
end


-----------------------------------------------------------------------------
-- Creates a WSAPI app for handling errors. This function tried to log the
-- error to the logger supplied in configuration. It then checks if it should
-- be displaying the stack trace to the user or not, and displays it if
-- appropriate.
-----------------------------------------------------------------------------
local function make_error_handling_app(config)
	return function(wsapi_env, err)
		-- Try to log this error.
		local ok, logger = copcall(zdc.logger.make_logger, config.LOGGER,
											config.LOGGER_PARAMS, config.LOGGER_LEVEL)
		if ok then
			logger:error(err)
		end

		-- Figure out what to tell the user.
		local summary = detect_common_errors(err, config)
							 or "ZWSAPI ran but failed due to an unexpected error."
		local message
		if config.sbenv.SHOW_STACK_TRACE then
			message = string.format(HTML_MESSAGE_WITH_STACK_TRACE, summary, err)
		else
			message = string.format(HTML_MESSAGE_WITHOUT_STACK_TRACE, summary)
		end

		-- Return the message
		response = wsapi.response.new()
		response:write(message)
		response.status = 500
		return response:finish()
	end
end


-----------------------------------------------------------------------------
-- Creates a WSAPI app for handling he ultimate FAIL: error in error handling.
--
-- This app gets called when the normal run failed, and then error
-- handling failed as well (presumably because the configuration is really
-- wrong). So, at this point it's just a matter of telling the user that
-- something went terribly wrong.
-----------------------------------------------------------------------------
local function make_oops_app()
	return function(wsapi_env)
		response = wsapi.response.new()
		response:write(ULTIMATE_OOPS_MESSAGE)
		response.status = 500
		return response:finish()
	end
end

-----------------------------------------------------------------------------
-- Creates a WSAPI app function to handle requests based on a configuration
-- table. This is the only function exported by the module. The app returned
-- by this function is _safe_ in the sense that errors do not get
-- reported to WSAPI but are instead handled by a cascade of error-handling
-- apps.
--
-- @param config			a bootstrap configuration for app.
-----------------------------------------------------------------------------
function new(modnew,config,hack)
	-- create three WSAPI app functions: one that we _want_ to run, another as
	-- a back up for the first (to report errors), and the third as a backup
	-- for the second (to deal with errors in error handling).
	local main_app = make_basic_zapp(modnew,config,hack)
	local error_app = make_error_handling_app(config,hack)
	local total_fail_app = make_oops_app(hack)
	return make_safer_app(main_app, error_app) --make_safer_app(error_app, total_fail_app))
end

